﻿using System;
using System.Collections.Generic;
using System.IO;
using BattleCity.Classes;
using BattleCity.GameObjects;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;

namespace BattleCity.GUI
{
    public class GameScreen : Screen
    {
        int gameOverTextSpeed = 2;
        int gameOverTextDelay = 1;

        private Rectangle scrBounds;

        //Текстуры карты
        Texture2D txtBack;
        Texture2D txtWall;
        Texture2D txtStone;
        Texture2D txtWater;
        Texture2D txtGrass;
        Texture2D txtIce;
        //Текстуры интерфейса
        Texture2D txtTankGUI;
        Texture2D txtLiveGUI;
        Texture2D txtFirstPlayerGUI;
        Texture2D txtLevelGUI;
        //Игрововой шрифт
        SpriteFont gameFont;

        //Массив для конструирования уровня
        List<string> Level;
        //Список типов врагов
        public List<byte> enemyList;
        //Список объектов типа танк
        public List<TankGUI> tankList;
        //Список мест появления врагов
        List<Vector2> enemiesPosition;
        //Список текстур бонусов
        List<Texture2D> bonusList;
        //Список анимаций игрока
        Dictionary<string, Animation> animList;
        //Список звуков игрока
        Dictionary<string, SoundEffect> soundList;
        //Двумерный массив анимаций на 8 типов врагов
        Dictionary<string, Animation>[] enemyAnimList;
        //Список звуков снаряда
        public Dictionary<string, SoundEffect> ShootSoundList;
        //Список анимаций снаряда
        public Dictionary<string, Animation> ShootAnimList;
        //Список анимаций базы
        List<Animation> baseAnimList;

        Rectangle recSprite = new Rectangle(0, 0, 16, 16);
        Rectangle recSprite32 = new Rectangle(0, 0, 32, 32);

        //Спрайты противников, базы и снаряда
        AnimationPlayer enemySprite;
        AnimationPlayer baseSprite;
        public AnimationPlayer shootSprite;

        //Загружаем звуки игры
        SoundEffect introSound;

        public Me player;
        public Base baseObj;

        bool isNewGame, gameNotStarted;
        private bool isGameOver;
        byte levelNumber;
        string path;
        //Число одновременно находящихся на карте танков противника
        public byte numberOfEnemies;
        //Общее число выпущенных на карту танков противника втечении уровня
        public byte enemiesNumber;
        //Общее число убитых врагов
        public byte destroyedEnemies;
        public List<byte> DestroyedEnemiesList;
        public int HiScore { get; set; }
        byte posNum;
        int count;
        Vector2 GSplashPos;
        Vector2 OSplashPos;
        double _splashTopPos, _waitTime;

        public event EventHandler GameOver;
        public event EventHandler LevelCompleted;

        public GameScreen(BattleCityGame _game, byte number, List<Texture2D> bon, Dictionary<string, Animation> me,
            Dictionary<string, SoundEffect> soundMe, Dictionary<string, Animation>[] enemyAnim,
            Dictionary<string, SoundEffect> shootSound, Dictionary<string, Animation> shootAnim, List<Animation> baseAnim,
            List<Texture2D> map, SoundEffect intro, List<Texture2D> iface, SpriteFont font)
            : base(_game)
        {
            //При создании объекта он по умолчанию невидим и неактивен
            Visible = false;
            Enabled = false;
            //получим объект SpriteBatch
            _sprBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
            scrBounds = new Rectangle(0, 0, 416, 416);
            isNewGame = true;
            gameNotStarted = true;
            levelNumber = number;
            enemiesPosition = new List<Vector2>(3);
            enemiesPosition.Add(new Vector2(0f, 0f));
            enemiesPosition.Add(new Vector2(13f, 0f));
            enemiesPosition.Add(new Vector2(24f, 0f));
            bonusList = bon;
            animList = me;
            soundList = soundMe;
            enemyAnimList = enemyAnim;
            ShootSoundList = shootSound;
            ShootAnimList = shootAnim;
            baseAnimList = baseAnim;
            txtWall = map[0];
            txtStone = map[1];
            txtWater = map[2];
            txtGrass = map[3];
            txtIce = map[4];
            introSound = intro;
            txtBack = iface[0];
            txtTankGUI = iface[1];
            txtLiveGUI = iface[2];
            txtFirstPlayerGUI = iface[3];
            txtLevelGUI = iface[4];
            gameFont = font;
            GSplashPos = new Vector2();
            OSplashPos = new Vector2();
            GSplashPos.X = OSplashPos.X = (_game.GameScreenSize.X - BattleCityGame.InfoPanelSize - gameFont.MeasureString("GAME").X) / 2;
            GSplashPos.Y = _game.GameScreenSize.Y + 2;
            OSplashPos.Y = GSplashPos.Y + gameFont.MeasureString("GAME").Y + 2;
            _splashTopPos = (_game.GameScreenSize.Y - (gameFont.MeasureString("GAME").Y * 2) - 2) / 2;
            DestroyedEnemiesList = new List<byte>();
        }
        //Функция считывает структуру уровня из файла и заносит ее в список строк Level
        void ConstructLevel()
        {
            path = String.Format("Content/Levels/{0}.txt", levelNumber);
            int width;
            using (StreamReader reader = new StreamReader(path))
            {
                string line = reader.ReadLine();
                int i = 0;
                width = line.Length;
                Level = new List<string>();
                enemyList = new List<byte>(20);
                Level.Add(line);
                while (i < 25)
                {
                    line = reader.ReadLine();
                    if (line.Length != width)
                        throw new Exception(String.Format("The length of line {0} is different from all preceeding lines.", Level.Count));
                    if (line == null)
                        throw new NullReferenceException(String.Format("Пустая строка ({0}) в файле с уровнем №{1}", i + 1, levelNumber));
                    Level.Add(line);
                    i++;
                }
                line = reader.ReadLine();
                foreach (char c in line)
                {
                    byte num = Convert.ToByte(c.ToString());
                    enemyList.Add(num);
                }

            }
        }
        //Функция расстановки объектов в игровом окне
        void AddSprites()
        {
            //Переменные для временного хранения адреса объекта-игрока
            int a = 0, b = 0;
            for (int i = 0; i < 26; i++)
            {
                for (int j = 0; j < 26; j++)
                {
                    //G - лес; b - база;
                    //# - кирпичная стена; @ - каменная стена;
                    //~ - вода; $ - лед; 1 - позиция игрока
                    if (Level[i][j] == 'G')
                        Game.Components.Add(new Grass(Game as BattleCityGame, ref txtGrass, new Vector2(j, i)));
                    if (Level[i][j] == 'b')
                    {
                        baseObj = new Base(Game as BattleCityGame, ref baseSprite, new Vector2(j, i), baseAnimList, soundList["dieSound"]);
                        baseObj.GameOver += new EventHandler(OnGameOver);
                        Game.Components.Add(baseObj);
                    }
                    if (Level[i][j] == '#')
                        Game.Components.Add(new Wall(Game as BattleCityGame, ref txtWall, new Vector2(j, i)));
                    if (Level[i][j] == '@')
                        Game.Components.Add(new Stone(Game as BattleCityGame, ref txtStone, new Vector2(j, i)));
                    if (Level[i][j] == '~')
                        Game.Components.Add(new Water(Game as BattleCityGame, ref txtWater, new Vector2(j, i)));
                    if (Level[i][j] == '$')
                        Game.Components.Add(new Ice(Game as BattleCityGame, ref txtIce, new Vector2(j, i)));
                    //Если обнаружен объект игрока - запишем его координаты
                    if (Level[i][j] == '1')
                    {
                        a = i;
                        b = j;
                    }
                }
            }
            //Последним установим объект игрока - так он гарантированно
            //расположен поверх всех остальных объектов (кроме леса)
            player = new Me(Game as BattleCityGame, ref _sprite, new Vector2(b, a), ref animList, ref soundList);
            player.GameOver+=new EventHandler(OnGameOver);
            Game.Components.Add(player);
        }
        //Функция, отвечающая за своевременное появление противников на карте 
        void AddEnemies()
        {
            if (enemiesNumber < 20)
            {
                if (numberOfEnemies < 3)
                {
                    byte eType = enemyList[enemiesNumber];
                    if (posNum > 2)
                        posNum = 0;
                    Game.Components.Add(new Enemy(Game as BattleCityGame, ref enemySprite, enemiesPosition[posNum], enemyAnimList, eType, Game.Content.Load<SoundEffect>("Sounds/kill"), player, baseObj));
                    posNum++;
                    numberOfEnemies++;
                    enemiesNumber++;
                }
                if (numberOfEnemies == 3 && count >= 400)
                {
                    byte eType = enemyList[enemiesNumber];
                    if (posNum > 2)
                        posNum = 0;
                    Game.Components.Add(new Enemy(Game as BattleCityGame, ref enemySprite, enemiesPosition[posNum], enemyAnimList, eType, Game.Content.Load<SoundEffect>("Sounds/kill"), player, baseObj));
                    posNum++;
                    numberOfEnemies++;
                    enemiesNumber++;
                }
            }
        }
        //Функция, добавляющая на поле рандомный бонус в рандомном месте
        public void AddBonuses()
        {
            Random rand = new Random();
            int bType = rand.Next(0, 5);
            Texture2D randBonusTexture = bonusList[bType];
            Game.Components.Add(new Bonus(Game as BattleCityGame, randBonusTexture, bType));
        }
        //Функция загружающая графический интерфейс
        void AddGUI()
        {
            tankList = new List<TankGUI>();
            for (int i = 0; i < 10; i++)
            {
                for (int j = 0; j < 2; j++)
                {
                    tankList.Add(new TankGUI(Game, ref txtTankGUI, new Vector2((27.0625f + j), (float)(1.0625f + i)), recSprite, (byte)(j + 1 + (2 * i))));
                    Game.Components.Add(tankList[(j + (2 * i))]);
                }
            }
        }
        //Функция, проверяющая состояние игры
        void CheckGameState()
        {
            if (destroyedEnemies == 20 && LevelCompleted != null)
                LevelCompleted(this, new EventArgs());
        }
        void OnGameOver(object sender, EventArgs e)
        {
            isGameOver = true;
        }
        //Функция, выводящая надпись Game Over
        void DrawGameOver(SpriteBatch spriteBatch, GameTime gameTime)
        {
            spriteBatch.DrawString(gameFont, "GAME", GSplashPos, Color.Red, 0f, new Vector2(), 1f, SpriteEffects.None, 0.55f);
            spriteBatch.DrawString(gameFont, "OVER", OSplashPos, Color.Red, 0f, new Vector2(), 1f, SpriteEffects.None, 0.55f);
            if (GSplashPos.Y >= _splashTopPos)
            {
                GSplashPos.Y -= gameOverTextSpeed;
                OSplashPos.Y -= gameOverTextSpeed;
                _waitTime = gameTime.TotalGameTime.TotalSeconds;
            }
            else
            {
                if (GameOver != null && gameTime.TotalGameTime.TotalSeconds - _waitTime > gameOverTextDelay)
                    GameOver(this, new EventArgs());
            }
        }
        public override void Update(GameTime gameTime)
        {
            if (gameNotStarted)
            {
                gameNotStarted = false;
                ConstructLevel();
                AddGUI();
                AddSprites();
            }
            ++count;
            if (isNewGame)
            {
                introSound.Play();
                isNewGame = false;
            }
            AddEnemies();
            CheckGameState();
            base.Update(gameTime);
        }
        public override void Draw(GameTime gameTime)
        {
            SpriteBatch spriteBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
            spriteBatch.Draw(txtBack, new Rectangle(0, 0, 480, 416), null, Color.White, 0f, new Vector2(0f, 0f), SpriteEffects.None, 1f);
            spriteBatch.Draw(txtLiveGUI, new Rectangle(430, 256, 16, 16), null, Color.White, 0f, new Vector2(0f, 0f), SpriteEffects.None, 0.9f);
            spriteBatch.Draw(txtFirstPlayerGUI, new Rectangle(430, 240, 32, 16), null, Color.White, 0f, new Vector2(0f, 0f), SpriteEffects.None, 0.9f);
            spriteBatch.Draw(txtLevelGUI, new Rectangle(428, 336, 32, 32), null, Color.White, 0f, new Vector2(0f, 0f), SpriteEffects.None, 0.9f);
            spriteBatch.DrawString(gameFont, levelNumber.ToString(), new Vector2(444f, 368f), Color.Black, 0f, new Vector2(0, 0), 1f, SpriteEffects.None, 0.9f);
            if (player.Lives >= 0 && player.Lives <= 50)
                spriteBatch.DrawString(gameFont, player.Lives.ToString(), new Vector2(448f, 258f), Color.Black, 0f, new Vector2(0, 0), 1f, SpriteEffects.None, 0.9f);
            else
                spriteBatch.DrawString(gameFont, "0", new Vector2(448f, 258f), Color.Black);
            if (isGameOver)
                DrawGameOver(spriteBatch, gameTime);
            base.Draw(gameTime);
        }
    }
}